#define PrynInternal
#define PrynImplementation
#include <pryn.h>

// Implemented by platform:
//	prynStreamOpenFile

#define PrynStreamBody(BODY) PrynBaseBody (stream, { state = stream->pState; BODY })
#define PrynStreamGetProperty(PropName, PropType, PropInitial, PropValue) PrynBaseGetProperty (, Stream, stream, PropName, PropType, PropInitial, PropValue)
#define PrynStreamSetProperty(PropName, PropType, PropAssign) PrynBaseSetProperty (, Stream, stream, PropName, PropType, PropAssign)

PrynStreamGetProperty (State, PrynState *, 0, { if (output) *output = state; })

static PrynResult DefaultCloseFunction (PrynStream *stream, void *data) { return PrynResultSuccess; }
static PrynResult DefaultReadFunction (PrynStream *stream, void *output, size_t length, size_t *read, void *data) { return PrynResultUnimplemented; }
static PrynResult DefaultWriteFunction (PrynStream *stream, const void *input, size_t length, size_t *written, void *data) { return PrynResultUnimplemented; }
static PrynResult DefaultSeekFunction (PrynStream *stream, PrynStreamPosition position, void *data) { return PrynResultUnimplemented; }
static PrynResult DefaultTellFunction (PrynStream *stream, PrynStreamPosition *position, void *data) { return PrynResultUnimplemented; }

PrynExport (PrynResult, prynStreamCreate, (PrynStream **output, PrynState *state))
{
	PrynStartFunctionHasState ();
	PrynStream *stream;

	if (output) *output = 0;
	prynAssert (output && state, NullArgument);

	prynCall (prynAllocateClearedObject (state, &stream));
	stream->pCommonObject.pType = PrynObjectTypeStream;
	stream->pState = state;
	stream->pCloseFunction = &DefaultCloseFunction;
	stream->pReadFunction = &DefaultReadFunction;
	stream->pWriteFunction = &DefaultWriteFunction;
	stream->pSeekFunction = &DefaultSeekFunction;
	stream->pTellFunction = &DefaultTellFunction;

	*output = stream;
	PrynEndFunction ();
}

PrynExport (PrynResult, prynStreamClose, (PrynStream *stream))
{
	PrynStartFunction ();

	prynAssert (stream, NullArgument);
	state = stream->pState;

	if (stream->pCloseFunction)
		stream->pCloseFunction (stream, stream->pCloseFunctionData);

	prynFree (state, stream);
	PrynEndFunction ();
}

PrynExport (PrynResult, prynStreamWriteExact, (PrynStream *stream, const void *data, size_t length, size_t *written))
{
	PrynStartFunction ();
	size_t writtenCopy;

	if (!written) written = &writtenCopy;
	prynCall (prynStreamWrite (stream, data, length, written));
	if (*written != length) return PrynResultStreamWriteFailure;
	PrynEndFunction ();
}

PrynExport (PrynResult, prynStreamWrite, (PrynStream *stream, const void *data, size_t length, size_t *written))
{
	PrynStartFunction ();
	size_t writtenCopy;

	if (written) *written = 0;
	else written = &writtenCopy;

	prynAssert (stream && data, NullArgument);
	prynCall (stream->pWriteFunction (stream, data, length, written, stream->pWriteFunctionData));
	if (*written != length && !result) prynReturnSuccessCode (StreamNotAllWritten);

	PrynEndFunction ();
}

PrynExport (PrynResult, prynStreamReadExact, (PrynStream *stream, void *data, size_t length, size_t *read))
{
	PrynStartFunction ();
	size_t readCopy;

	if (!read) read = &readCopy;
	prynCall (prynStreamRead (stream, data, length, read));
	if (*read != length) return PrynResultStreamReadFailure;
	PrynEndFunction ();
}

PrynExport (PrynResult, prynStreamRead, (PrynStream *stream, void *data, size_t length, size_t *read))
{
	PrynStartFunction ();
	size_t readCopy;

	if (read) *read = 0;
	else read = &readCopy;

	prynAssert (stream && data, NullArgument);
	prynCall (stream->pReadFunction (stream, data, length, read, stream->pReadFunctionData));
	if (*read != length && !result) prynReturnSuccessCode (StreamNotAllRead);

	PrynEndFunction ();
}


#if 0

PrynExport (PrynResult, prynStreamRead, (PrynStream *stream, void *data, size_t length, size_t *read))
	/**< @brief Read a number of bytes from the stream.

	@param stream The stream to read from.
	@param[out] data Written with the data read from the stream; must be at least @e length bytes of valid writable data. If not, the program will crash, with no attempt made (or seriously possible) to prevent that from happening.
	@param length The number of bytes to attempt to read.
	@param[out] read If non-null, written with the number of bytes read from the stream, or 0 on failure.
	@returns #PrynResultSuccess or an error code; see #PrynResult. Reading less than the requested number of bytes returns the success condition #PrynResultStreamNotAllRead. Use #prynStreamReadExact to return the error code #PrynResultStreamReadFailure on that condition. */

PrynExport (PrynResult, prynStreamReadExact, (PrynStream *stream, void *data, size_t length, size_t *read))

#endif